import requests
import time
import hashlib
import json
import collections
import datetime
from threading import RLock
from frappe.utils import now
import frappe

class MisClient(object):
    single_lock = RLock()
    __base_url = None
    __app_key = None
    __app_secret = None
    __authorization = None
    __last_login_date = None
    __expire = None


    def __new__(cls, *args, **kwargs):
        with MisClient.single_lock:
            if not hasattr(MisClient, "_instance"):
                MisClient._instance = object.__new__(cls)
        return MisClient._instance
    

    def __init__(self):
        super().__init__()
        l = frappe.get_all('SRM Third API Config', filters={'disable': 0}, fields=['name'])
        s = frappe.get_doc('SRM Third API Config', l[0].name)
        if s:
            self.__base_url = s.server_url
            self.__app_key = s.app_key
            self.__app_secret = s.app_secret
            self.__authorization = s.authorization if s.authorization else ''
            self.__expire = s.expire if s.expire else 120
        else:
            frappe.throw('API接口参数未配置!')

    def __get_header(self):
        return {
            'Content-Type': 'application/json',
            'Accept': '*/*',
            'authorization':self.__authorization
        }

    def executeRequest(self,url,params,data,method):
        if (self.__authorization == '' or self.__authorization == None or self.__last_login_date == None) or ((datetime.datetime.now() - self.__last_login_date).seconds > 120*60):
            self.login()

        response = method(self.__base_url + url, params= params, json= data, headers=self.__get_header())
        if(response.status_code != 200):
            raise Exception("请求失败")
        data = json.loads(response.text)

        returnObj = ReturnObject()
        returnObj.__dict__ = data
        return returnObj

    def post(self,url,params,data):        
        return self.executeRequest(url,params,data,requests.post)      

    def get(self,url,params,data):
        return self.executeRequest(url,params,data,requests.get)
    
    def put(self,url,params,data):
        return self.executeRequest(url,params,data,requests.put)

    def login(self):  
        login_url =  self.__base_url + '/auth/open/login'
        systemRequestParam = self.GetSignSystemRequestParam()
        response = requests.post(login_url,params= systemRequestParam.toDict())
        if(response.status_code != 200):
            raise Exception('请求失败!{status_code}'.format(status_code = response.status_code))
        
        jsonData = json.loads(response.text)

        returnObject = ReturnObject()
        returnObject.__dict__ = jsonData

        if(returnObject.code != 200):
            raise Exception('鉴权失败!{msg}'.format(msg = returnObject.msg))    
        
        data = returnObject.data
        token = data['access_token']
        expire = data['expires_in']

        self.__authorization = token
        self.__last_login_date = datetime.datetime.now()
        self.__expire = expire


    def GetKeyValueStr(self,systemRequestParam):
        temp_dict = {}
        if(systemRequestParam.params):
            temp_dict.update(systemRequestParam.params)
        
        if(systemRequestParam.body):
            temp_dict["requestBody"]=json.dumps(systemRequestParam.body)

        temp_dict['appKey']  = systemRequestParam.appKey
        temp_dict['timestamp']  = systemRequestParam.timestamp

        order_dict = collections.OrderedDict(sorted(temp_dict.items(), key=lambda t: t[0]))

        keyvalue = self.__app_secret
        for key, value in order_dict.items():
            keyvalue += key + str(value)
        keyvalue = keyvalue + self.__app_secret

        return keyvalue
        
    
    def GetSignSystemRequestParam(self, params = None,body = None):
        systemParams = SystemRequestParam(params,body)

        systemParams.appKey = self.__app_key
        systemParams.timestamp = int(time.time()) * 1000
        
        keyvalue = self.GetKeyValueStr(systemParams)
 
        sign = hashlib.md5(keyvalue.encode(encoding='utf-8')).hexdigest().upper()
        systemParams.sign = sign
        return systemParams



class SystemRequestParam(object):
    
    def __init__(self,params= None,body=None):
        self.appKey = ''
        self.timestamp = ''
        self.sign = ''
        self.params = params
        self.body = body

    def toDict(self):
        if not self.params:
            self.params = {}

        self.params['appKey'] = self.appKey
        self.params['timestamp'] = self.timestamp
        self.params['sign'] = self.sign

        return self.params
    

class ReturnObject(object):
    def __init__(self):
        self.code = 0
        self.msg = ''
        self.data = {}



class MisApi(object):
    def __init__(self):
        self.client = MisClient()


    def bill_save(self,data):
        url = '/umflowable/open/bill/save'
        systemRequestParam = self.client.GetSignSystemRequestParam(body=data)
        returnObj = self.client.post(url,systemRequestParam.toDict(),systemRequestParam.body)
        if(returnObj.code != 200):
            error = frappe.get_doc({'doctype': 'Error Log', 'method': 'sync_to_oa_workfolw', 'error': '保存失败!{msg}'.format(msg = returnObj.msg)})
            error.insert(ignore_permissions=True)
            # frappe.throw('保存失败!{msg}'.format(msg = returnObj.msg))
            # raise Exception('保存失败!{msg}'.format(msg = returnObj.msg))
        
        data = returnObj.data
        return data


    def call_back(self,data):
        url = '/umflowable/open/bill/callback'
        systemRequestParam = self.client.GetSignSystemRequestParam(params=data)
        returnObj = self.client.put(url,systemRequestParam.toDict(),systemRequestParam.body)
        if(returnObj.code != 200):
            # raise Exception('撤回!{msg}'.format(msg = returnObj.msg))
            frappe.throw('撤回失败!{msg}'.format(msg = returnObj.msg))
        
        data = returnObj.data
        return data


    def bill_query(self,data):
        url = '/umflowable/open/bill/status'
        systemRequestParam = self.client.GetSignSystemRequestParam(params=data)
        returnObj = self.client.get(url,systemRequestParam.toDict(),systemRequestParam.body)
        if(returnObj.code != 200):
            frappe.throw('查询失败!{msg}'.format(msg = returnObj.msg))
            # raise Exception('查询失败!{msg}'.format(msg = returnObj.msg))
        
        data = returnObj.data
        return data
